package whf.framework.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Map;

import javax.sql.DataSource;

import whf.framework.log.Log;
import whf.framework.log.LogFactory;
import whf.framework.resource.db.DBHelper;

/**
 * 生成id
 * @author wanghaifeng
 * 
 */
public class Sequence {
	private static Log log  = LogFactory.getLog(Sequence.class);
	
	private static DataSource dataSource = null;
	
	private static Map<String, SequenceItem> cache = Utils.newHashMap();
	
	private static Connection getConnection() throws SQLException{
		if(dataSource == null){
			dataSource = BeanFactory.getDataSource();
		}
		return dataSource.getConnection();
	}
	
	public static long nextId(String category) {
		long result = -1;
		synchronized(category) {
			try {
				SequenceItem item = cache.get(category);
				if (item == null) {
					item = new SequenceItem(category);
					cache.put(category, item);
				}
				result = item.nextValue();
			} catch (Exception e) {
				log.error(e);
			}
		}
		log.debug(category + "\t" + result);
		return result;
	}
	
	private static class SequenceItem {
		private String sequenceType;
		
		private long currentSequenceValue;
		
		private long maxStepSequenceValue;
		
		private int step = 10;
		
		public SequenceItem(String sequenceType) throws Exception {
			this.sequenceType = sequenceType;
			Connection con  = null;
			PreparedStatement pstmt = null;
			ResultSet rs = null;
			try{
				con = getConnection();
				pstmt = con.prepareStatement("select * from framework_id_table where category=?");
				pstmt.setString(1, sequenceType);
				rs = pstmt.executeQuery();
				if(rs.next()) {
					if(rs.getString("step") != null)
						this.step = rs.getInt("step");
					this.currentSequenceValue = rs.getLong("cur_value");
				} else {
					pstmt = con.prepareStatement("insert into framework_id_table(category, cur_value, step, last_update) values(?,?,?,?)");
					pstmt.setString(1, sequenceType);
					pstmt.setLong(2, 0L);
					pstmt.setInt(3, this.step);
					pstmt.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
					pstmt.executeUpdate();
					this.currentSequenceValue = 0L;
				}
				if(this.step <= 0) this.step = 1;
			}finally {
				DBHelper.close(rs);
				DBHelper.close(pstmt);
				DBHelper.close(con);
			}
		}
		
		public long nextValue() throws Exception {
			this.currentSequenceValue ++;
			if(this.currentSequenceValue <= this.maxStepSequenceValue) {
				return this.currentSequenceValue;
			} else {
				Connection con = null;
				PreparedStatement pstmt = null;
				while(this.maxStepSequenceValue < this.currentSequenceValue) {
					this.maxStepSequenceValue +=  this.step;
				}
				try{
					con = getConnection();
					pstmt = con.prepareStatement("update framework_id_table set cur_value=?, step=?, last_update=? where category=?");
					pstmt.setLong(1, this.maxStepSequenceValue);
					pstmt.setInt(2, this.step);
					pstmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
					pstmt.setString(4, this.sequenceType);
					pstmt.executeUpdate();
				} finally {
					DBHelper.close(pstmt);
					DBHelper.close(con);
				}
				return this.currentSequenceValue;
			}
		}
	}
	
	public static void main(String[] args) throws Exception{
		for(int i=1; i< 100; i++){
			System.out.println(Sequence.nextId("TEST"));
		}
	}
}